﻿using System;
using Oxage.Common;
using Oxage.Forms;
using Oxage.Midi;
using Sanford.Multimedia.Midi;

namespace Oxage
{
	public class SanfordMidiConnector : IConnector
	{
		#region Members
		private WindowsMidiForm form;
		private OutputDevice outDevice;
		private InputDevice inDevice;
		private bool isOpened = false;
		#endregion

		#region Constructor
		public SanfordMidiConnector()
		{
			this.MidiPort = 0;
			this.Instrument = 0x01;
			this.LastInputChannel = 0;
			this.form = new WindowsMidiForm(this);
		}
		#endregion

		#region Properties
		public int MidiPort
		{
			get;
			set;
		}

		public byte Instrument
		{
			get;
			set;
		}

		public byte LastInputChannel
		{
			get;
			set;
		}
		#endregion

		#region Methods
		public override string ToString()
		{
			return "Sanford MIDI";
		}
		#endregion

		#region Event handlers
		protected void inDevice_ChannelMessageReceived(object sender, ChannelMessageEventArgs e)
		{
			var msg = new MidiMessage();
			msg.Channel = (byte)e.Message.MidiChannel;
			msg.Note = (byte)e.Message.Data1;
			msg.Velocity = (byte)e.Message.Data2;
			OnReceived(msg);
		}
		#endregion

		#region IConnector Members
		public bool IsOpened
		{
			get
			{
				return this.isOpened;
			}
			set
			{
				this.isOpened = value;
				this.form.IsLocked = value;
			}
		}

		public DataDirection Direction
		{
			get { return DataDirection.Both; }
		}

		public void Open()
		{
			Close();
			outDevice = new OutputDevice(this.MidiPort);
			inDevice = new InputDevice(this.MidiPort);
			inDevice.ChannelMessageReceived += new EventHandler<ChannelMessageEventArgs>(inDevice_ChannelMessageReceived);
			ProgramChange(this.Instrument);
			this.IsOpened = (outDevice != null && inDevice != null);
		}

		public void Close()
		{
			if (outDevice != null)
			{
				outDevice.Close();
			}
			if (inDevice != null)
			{
				inDevice.ChannelMessageReceived -= new EventHandler<ChannelMessageEventArgs>(inDevice_ChannelMessageReceived);
				inDevice.Close();
			}
			this.IsOpened = false;
		}

		public void Send(IMessage message)
		{
			Send(message as MidiMessage);
		}

		public void Send(MidiMessage message)
		{
			if (this.IsOpened && message != null)
			{
				try
				{
					this.LastInputChannel = message.Channel;

					ChannelMessage e = null;
					switch (message.Event)
					{
						case MidiEvents.NoteOn:
							e = new ChannelMessage(ChannelCommand.NoteOn, message.Channel, message.Note, message.Velocity);
							break;

						case MidiEvents.NoteOff:
							e = new ChannelMessage(ChannelCommand.NoteOff, message.Channel, message.Note, message.Velocity);
							break;

						case MidiEvents.ProgramChange:
							e = new ChannelMessage(ChannelCommand.ProgramChange, message.Channel, message.Note, message.Velocity);
							break;
					}
					if (e != null)
					{
						outDevice.Send(e);
					}
				}
				catch
				{
				}
			}
		}

		public void ProgramChange(int instrument)
		{
			if (this.IsOpened)
			{
				outDevice.Send(new ChannelMessage(ChannelCommand.ProgramChange, this.LastInputChannel, instrument, 127));
			}
		}

		public void ProgramChange(MidiProgram program)
		{
			ProgramChange(program.Number);
		}

		public event EventHandler<EventArgs<IMessage>> Received;
		protected virtual void OnReceived(IMessage message)
		{
			if (Received != null)
			{
				Received(this, new EventArgs<IMessage>(message));
			}
		}

		public IConfigPanel GetConfigPanel()
		{
			return form;
		}
		#endregion
	}
}
